{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "12558730",
   "metadata": {},
   "source": [
    "## 2.1 基础知识"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dbcd782",
   "metadata": {},
   "source": [
    "### 2.1.1 数值基础\n",
    "- 在python中，一共内置三种数值类型：整形（int）、浮点型（float）和复数类型（complex）\n",
    "- 在定义数值面量时，当数字比较长时，常插入_分隔符来让它变的更易读\n",
    "- 计算机对小于1的浮点数做不到绝对的精准"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d11588c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "0.1+0.2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1c2ff61",
   "metadata": {},
   "source": [
    "- 为了解决这个问题，Python内置了decimal模块。如果要精确计算浮点数，可以使用decimal.Decimal对象代替普通浮点数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f5850e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from decimal import Decimal\n",
    "Decimal('0.1')+Decimal('0.2')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b34abed8",
   "metadata": {},
   "source": [
    "- **注意必须使用字符串表示数字**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b5164d1",
   "metadata": {},
   "source": [
    "### 2.1.2 布尔值其实也是数字\n",
    "**True**和**False**这两个布尔值可以当作1和0来使用，这个特点常用来简化总数统计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e021d08e",
   "metadata": {},
   "outputs": [],
   "source": [
    "numbers = [1, 2, 4, 5, 7]\n",
    "count = sum(i % 2 == 0 for i in numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f636a114",
   "metadata": {},
   "source": [
    "### 2.1.3 字符串常用操作\n",
    "1. 把字符串当序列来操作\n",
    "- 字符串是一种序列类型，这意味着你可以对他进行遍历，切片等操作，就像访问一个列表对象一样\n",
    "- 假设想反转一个字符串，可以使用切片操作或者reversed内置方法\n",
    "- 切片最后一个字段使用-1，表示从后往前反序\n",
    "- **reversed**会返回一个可迭代对象，通过字符串的 **.join** 方法可以将其转换为字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd46137b",
   "metadata": {},
   "outputs": [],
   "source": [
    "s = \"Hello world!\"\n",
    "for c in s:\n",
    "    print(c)\n",
    "s[1:3]\n",
    "s[::-1]\n",
    "''.join(reversed(s))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32dfff00",
   "metadata": {},
   "source": [
    "2. 字符串格式化\n",
    "- 三种字符串格式化方法\n",
    "  - C语言风格的基于百分号%的格式化语句\n",
    "  - 新式字符串格式化（str.format）方式\n",
    "  - f-string字符串字面量格式化表达式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "252c4da9",
   "metadata": {},
   "outputs": [],
   "source": [
    "'Hello, %s'% 'World'\n",
    "\"Hello, {}\".format('world')\n",
    "name = 'World'\n",
    "f'Hello, {name}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18dc6a1d",
   "metadata": {},
   "source": [
    "- str.format与f-string共享了一种复杂的字符串格式化微语言，通过这种微语言，可以对字符串二次加工"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea9b36ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "username, score = 'piglei', 100\n",
    "print('{:>20}'.format(username))\n",
    "print(f'{username:>20}')\n",
    "#str.format的独到之处\n",
    "print('{0}: name = {0} score = {1}'.format(username, score))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a7c514a",
   "metadata": {},
   "source": [
    "3. 拼接多个字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b43ab125",
   "metadata": {},
   "outputs": [],
   "source": [
    "words = ['Number(1-10):']\n",
    "for i in range(10):\n",
    "    words.append(f'Value: {i+1}')\n",
    "print('\\n'.join(words))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c070f4ce",
   "metadata": {},
   "source": [
    "### 2.1.4 不常用但特别好用的字符串方法\n",
    "- 常用的字符串方法：**.join()、.split()、.startswitch()**\n",
    "- 不常用的字符串： **str.partition(), str.translate()**\n",
    "- 处理字符串的时候，尽量先找找有没有功能相似的函数，没必要重复造轮子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b62ec282",
   "metadata": {},
   "outputs": [],
   "source": [
    "def extract_value(s):\n",
    "    items = s.splite(':')\n",
    "    if len(items) == 2:\n",
    "        return items[1]\n",
    "    else:\n",
    "        return ''\n",
    "def extract_value_v2(s):\n",
    "    return s.partition(':')[-1]\n",
    "s = '明明是中文,却用了英文的标点符号.'\n",
    "table = s.maketrans(',.', '，。')\n",
    "s.translate(table)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53044546",
   "metadata": {},
   "source": [
    "### 2.1.5 字符串与字节串\n",
    "- 广义上的“字符串”概念可分为两类\n",
    "  - 字符串：“普通字符串”，有时被称为文本，是给人看的，对应Python中的字符串（str）类型。str使用Unicode标准，可通过.encode()方法编码为字节串\n",
    "  - 字节串：有时也称“二进制字符串”，是给计算机看的，对应Python中的字节串（bytes）类型。bytes一定包含某种真正的字符串编码格式（默认为UTF-8），可通过.decode解码为字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f58f0272",
   "metadata": {},
   "outputs": [],
   "source": [
    "str_obj = 'Hello, 世界'\n",
    "bin_str = str_obj.encode('UTF-8')\n",
    "type(str_obj)\n",
    "type(bin_str)\n",
    "str_obj.encode('UTF-8') == str_obj.encode()\n",
    "str_obj.encode('gbk')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a386b9f1",
   "metadata": {},
   "source": [
    "- 在程序中尽量保证使用普通字符串，而非字节串。必须操作字节串的场景，一般只有两种：\n",
    "  1. 程序从文件或其他外部存储读取字节串内容，将其解码为字符串，然后在内部使用\n",
    "  2. 程序完成处理，要把字符串写入文件和其他外部存储，将其编码为字节串，然后继续执行其他操作\n",
    "- 将字符串写入文件时，一定要将其编码为字节串\n",
    "- 字符串简单使用的原因在于用一个“边缘转换层”把人和计算机隔开"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2a82098",
   "metadata": {},
   "outputs": [],
   "source": [
    "def upper_s(s):\n",
    "    \"\"\"把输入字符串中所有的s都转为大写\"\"\"\n",
    "    return s.replace('s', 'S')\n",
    "with open('output.txt', 'w', encoding = 'UTF-8') as fp:\n",
    "    str_obj = upper_s('super sunflowers(向日葵)')\n",
    "    fp.write(str_obj)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d5fdfbd",
   "metadata": {},
   "source": [
    "## 2.2 案例故事\n",
    "### 2.2.1 代码里的“密码”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "227b2530",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_daily_points(user):\n",
    "    \"\"\"用户每天完成一次登录后，为其增加积分\"\"\"\n",
    "    if user.type == 13:\n",
    "        return \n",
    "    if user.type == 3:\n",
    "        user.points += 120\n",
    "        return \n",
    "    user.points += 100\n",
    "    return "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b00d750",
   "metadata": {},
   "source": [
    "- 密码的含义\n",
    "    - 13:用户type是13，代表处于封禁状态，不能增加积分\n",
    "    - 3：用户type为3，代表用户充值了VIP\n",
    "    - 100：普通用户每天登录增加100积分\n",
    "    - 120：VIP用户每天登录增加120积分"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a600ef43",
   "metadata": {},
   "source": [
    "2. 改善代码的可读性\n",
    "\n",
    "- 枚举类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c01318c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from enum import Enum\n",
    "\n",
    "#在定义枚举类型时，如果同时继承一些基础类型，比如int,str,\n",
    "#枚举类型能同时充当该基础类型使用。比如在这里，UserType就可以当作int使用\n",
    "class UserType(int, Enum):\n",
    "    #VIP用户\n",
    "    VIP = 3\n",
    "    #小黑屋用户\n",
    "    BANNED = 13\n",
    "DAILY_POINTS_REWARDS = 100\n",
    "def add_daily_points(user):\n",
    "    if user.type == UserType.BANNED:\n",
    "        return\n",
    "    if user.type == UserType.VIP:\n",
    "        user.point += 120\n",
    "    user.points += DAILY_POINTS_REWARDS"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "439042bc",
   "metadata": {},
   "source": [
    "- 常量和枚举量代替字面量的好处：\n",
    "    - 更易读：所有人不用记忆某个数字代表什么\n",
    "    - 更健壮：降低输错数字和字母产生bug的可能性"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c8706f7",
   "metadata": {},
   "source": [
    "### 2.2.2 别轻易成为SQL语句“大师”\n",
    "- 裸字符串处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "385e77b1",
   "metadata": {},
   "outputs": [],
   "source": [
    "def fetch_users(\n",
    "        conn,\n",
    "        min_level = None,\n",
    "        gender  = None,\n",
    "        has_membership = False,\n",
    "        sort_field = \"created\",\n",
    "    ):\n",
    "    \"\"\"获取用户列表\n",
    "    :param min_lever:要求的最低用户等级，默认为所有级别\n",
    "    :type min_lever: int, optional\n",
    "    :param gender:筛选用户性别，默认所有性别\n",
    "    :type gender: int optional\n",
    "    :param has_membership:筛选会员和非会员用户，默认为False，代表非会员\n",
    "    ：type has_membership:bool, optional\n",
    "    :param sort_field:排序字段，默认为“created”，代表按用户创建日期排序\n",
    "    ：type sort_field:str, optional\n",
    "    :return:一个包含用户信息的列表：[(User ID, User Name), ...]\n",
    "    \"\"\"\n",
    "    #一种古老的SQL拼接技巧，使用“WHERE 1=1”来简化字符串拼接操作\n",
    "    statement = \"SELECT id, name FROM users WHERE 1=1\"\n",
    "    params = []\n",
    "    if min_level is not  None:\n",
    "        statement += \"AND level >= ?\"\n",
    "        params.append(min_level)\n",
    "    if gender is not None:\n",
    "        statement += \"AND gender >=?\"\n",
    "        params.append(gender)\n",
    "    if has_membership:\n",
    "        statement += \"AND has_membership = true\"\n",
    "    else:\n",
    "        statement += \"AND has_membership = false\"\n",
    "\n",
    "    statement += \"ORDER BY ?\"\n",
    "    params.append(sort_field)\n",
    "    #将查询参数params作为位置参数传递，避免SQL注入问题\n",
    "    return list(conn.execute(statement, params))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c627e02",
   "metadata": {},
   "source": [
    "1. 使用SQLAIchemy模块改写代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6bc6666",
   "metadata": {},
   "outputs": [],
   "source": [
    "def fetch_user_v2(\n",
    "        conn,\n",
    "        min_level = None,\n",
    "        gender  = None,\n",
    "        has_membership = False,\n",
    "        sort_field = \"created\",\n",
    "):\n",
    "    query = select([users.c.id, users.c.name])\n",
    "    if min_level != None:\n",
    "        query = query.where(users.c.level >= min_level)\n",
    "    if gender != None:\n",
    "        query = query.where(users.c.gender == gender)\n",
    "    query = query.where(users.c.has_membership == has_membership).order_by(\n",
    "        users.c[sort_field]\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe13b797",
   "metadata": {},
   "source": [
    "2. 使用Jinja2模块处理字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0abf0778",
   "metadata": {},
   "outputs": [],
   "source": [
    "def render_movies(username, movies):\n",
    "    \"\"\"\n",
    "    以文本方式展示电影列表信息\n",
    "    \"\"\"\n",
    "    welcome_text = 'Welcome, {}.\\n'.format(username)\n",
    "    text_parts = [welcome_text]\n",
    "    for name, rating in movies:\n",
    "        #没有提供评分的电影，以[NOT RATED]代替\n",
    "        rating_text = rating if rating else '[NOT RATED]'\n",
    "        movie_item = '{}, Rating: {}'.format(name, rating_text)\n",
    "        text_parts.append(movie_item)\n",
    "    return '\\n'.join(text_parts)\n",
    "\n",
    "movies = [\n",
    "    ('The shawshank Redemption', '9.3'),\n",
    "    ('The Prestinge', '8.5'),\n",
    "    ('Mulan', None)\n",
    "]\n",
    "\n",
    "print(render_movies('piglei', movies))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "76d28f49",
   "metadata": {},
   "outputs": [],
   "source": [
    "from jinja2 import Template\n",
    "\n",
    "_MOVIES_TMPL = '''\\\n",
    "Welcome, {{username}}.\n",
    "{%for name, rating in movies %}\n",
    "*{{name}}, Rating: {{ rating|default(\"[NOT RATED]\", True)}}\n",
    "{%- endfor %}\n",
    "'''\n",
    "def render_movie_j2(username, movies):\n",
    "    tmp1 = Template(_MOVIES_TMPL)\n",
    "    return tmp1.render(username = username, movies = movies)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fa5f367",
   "metadata": {},
   "source": [
    "- 如果目标原字符串是遵循某种格式，可以先寻找是否有对应的开源专有模块，比如处理SQL语句的SQLAlchemy，处理XML的lxml模块等"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a84d409",
   "metadata": {},
   "source": [
    "## 2.3 编程建议"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "723764cd",
   "metadata": {},
   "source": [
    "### 2.3.1 不必预计算字面量表达式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35a41e6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def do_somethong(delta_seconds):\n",
    "    #如果时间已经过去11天（或者更久），不做任何事\n",
    "    if delta_seconds > 950400:\n",
    "        return"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2773706",
   "metadata": {},
   "source": [
    "- 使用dis模块反编译字节码\n",
    "    1. 两次LOAD_FAST：分别把局部变量x和y的值放入栈顶\n",
    "    2. BINARY_ADD：从栈顶取出两个值（也就是x和y的值），执行加法操作，将结果放回栈顶\n",
    "    3. RETURN_VALUE:返回栈顶的结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "355cc5bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, y):\n",
    "    return x+y\n",
    "import dis\n",
    "dis.dis(add)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0908cdc5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def do_something(delta_seconds):\n",
    "    #如果时间已经过去11天（或者更久），不做任何事\n",
    "    if delta_seconds > 950400:\n",
    "        return\n",
    "def do_something1(delta_seconds):\n",
    "    #如果时间已经过去11天（或者更久），不做任何事\n",
    "    if delta_seconds > 11*24*3600:\n",
    "        return\n",
    "import dis\n",
    "dis.dis(do_something)\n",
    "dis.dis(do_something1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4373c38",
   "metadata": {},
   "source": [
    "- 所以 **11x24x36** 只执行一次，对性能没影响，且更易读"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "707dcb08",
   "metadata": {},
   "source": [
    "### 2.3.2 使用特殊数字 “无穷大”\n",
    "- float(\"-inf\") < 任意数值 < float(\"inf\") "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b11edfcd",
   "metadata": {},
   "outputs": [],
   "source": [
    "def sort_users_inf(users):\n",
    "\n",
    "    def key_func(username):\n",
    "        age = users[username]\n",
    "        #当年龄为空时，返回正无穷大作为key，因此就会被排到最后\n",
    "        return age if age is not None else float('inf')\n",
    "    \n",
    "    return  sorted(users.keys(), key = key_func)\n",
    "\n",
    "users = {\"tom\": 19, \"jenny\": 13, \"jack\": None, \"andrew\": 43}\n",
    "print(sort_users_inf(users))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8634b055",
   "metadata": {},
   "source": [
    "### 2.3.3 改善超长字符串的可读性\n",
    "1. 使用三引号 \"\"\" \"\"\"\n",
    "2. 使用加号 \n",
    "3. 使用元组或者列表存储后拼接 ''.jion(str)\n",
    "4. 使用反斜杠\\ \n",
    "    ```python\n",
    "    \"str1\\  \n",
    "        str2\"\n",
    "        ```\n",
    "5. 使用括号将长字符串包起来\n",
    "    ```python\n",
    "    s = (\"str1\"\n",
    "        \"str2\"\n",
    "    )\n",
    "    ```\n",
    "6. 多级缩进里出现多行字符串,使用标准库textwrap解决这个问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e82ff4d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from textwrap import dedent\n",
    "\n",
    "def main():\n",
    "    if user.is_active:\n",
    "        message = dedent(\"\"\"\\\n",
    "        welcome, today's movie list:\n",
    "        -jaw (1975)\n",
    "        -The shinning (1980)\n",
    "        -saw (2024)\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6459e1c4",
   "metadata": {},
   "source": [
    "### 2.3.4 别忘了以r开头的字符串内置方法\n",
    "从右往左的逆序方法：rsplit()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0900914",
   "metadata": {},
   "source": [
    "### 2.3.5 不要害怕字符串拼接\n",
    "timeit模块测试代码的执行效率"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "vs_script",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
